﻿using NicoView.Model.Browser;
using NicoView.Model.Service.Comments;
using NicoView.Model.Service.Errors;
using NicoView.Model.Service.Files;
using NicoView.Model.Service.Property;
using NicoView.Model.Share.Browser;
using NicoView.Model.Speech;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NicoView.Model.Service
{
    /// <summary>
    /// にこびゅプロパティ管理クラス
    /// </summary>
    public class NicoViewProperty:NicoViewObservableObject
    {

        #region コンストラクタ

        internal NicoViewProperty()
        {
            saveAbleProperty_ = new SaveAbleProperty<NicoViewRomingProperty>();
            saveAblelocalProperty_ = new SaveAbleProperty<NicoViewLocalProperty>();
            saveAbleProperty_.Propety = new NicoViewRomingProperty();
            saveAblelocalProperty_.Propety = new NicoViewLocalProperty();
            saveAbleBroadcastProperty = new SaveAbleProperty<NicoViewBroadcastProperty>();

        }


        #endregion

        #region イベント

        public new event PropertyChangedEventHandler PropertyChanged
        {
            add
            {
                RomingProperty.PropertyChanged += value;
                LocalProperty.PropertyChanged += value;
            }
            remove
            {
                RomingProperty.PropertyChanged -= value;
                LocalProperty.PropertyChanged -= value;
            }
        }

        #endregion

        #region プロパティ・メンバ


        /// <summary>
        /// コメントアート表示
        /// </summary>
        public bool VisibleCommentArtComment
        {
            get
            {
                return this.RomingProperty.VisibleCommentArtComment;
            }

            set
            {
                this.RomingProperty.VisibleCommentArtComment = value;
            }
        }

        /// <summary>
        /// 184コメント表示
        /// </summary>
        public bool VisibleAnonymous
        {
            get
            {

                return this.RomingProperty.VisibleAnonymous;
            }

            set
            {
                this.RomingProperty.VisibleAnonymous = value;
            }
        }

        /// <summary>
        /// 次枠自動生成するか判定
        /// </summary>
        public bool AutoCreateNextBroadcast
        {
            get
            {
                return this.RomingProperty.AutoCreateNextBroadcast;
            }
            set
            {
                this.RomingProperty.AutoCreateNextBroadcast = value;
            }
        }

        /// <summary>
        /// にこびゅプロパティオブジェクト
        /// </summary>
        private NicoViewRomingProperty RomingProperty
        {
            get
            {
                return saveAbleProperty.Propety;
            }


        }

        /// <summary>
        /// 配信関連プロパティ
        /// </summary>
        public NicoViewBroadcastProperty BroadcastProperty
        {

            get
            {
                return this.saveAbleBroadcastProperty.Propety;
            }
        }



        /// <summary>
        /// 配信にエンコーダを使用する
        /// </summary>
        public bool UsingBroadcasatEncoder
        {
            get
            {
                return this.RomingProperty.UsingBroadcasatEncoder;
            }

            set
            {
                this.RomingProperty.UsingBroadcasatEncoder = value;
            }
        }

        /// <summary>
        /// 読上の学習が有効か判定
        /// </summary>
        public bool SpeechLearnEnable
        {
            get
            {
                return this.RomingProperty.SpeechLearnEnable;
            }

            set
            {
                this.RomingProperty.SpeechLearnEnable = value;
            }
        }

        /// <summary>
        /// 184コメント送信か判定
        /// </summary>
        public bool AnonymousSend
        {
            get
            {
                return this.RomingProperty.AnonymousSend;
            }
            set
            {
                this.RomingProperty.AnonymousSend = value;
            }
        }

        /// <summary>
        /// 主コメント送信か判定
        /// </summary>
        public bool OwnerSend
        {
            get
            {
                return this.RomingProperty.OwnerSend;
            }

            set
            {
                this.RomingProperty.OwnerSend = value;
            }
        }

        /// <summary>
        /// NGコメントを回避して書き込みをを行うか判定
        /// </summary>
        public bool NgAvoidance
        {
            get
            {
                return this.RomingProperty.NgAvoidance;
            }

            set
            {
                this.RomingProperty.NgAvoidance = value;
            }
        }

        /// <summary>
        /// @のコメントを固定ハンドルネームにするか判定
        /// </summary>
        public bool AtMarkNameToCustomName
        {
            get
            {
                return this.RomingProperty.AtMarkNameToCustomName;
            }
            set
            {
                this.RomingProperty.AtMarkNameToCustomName = value;
            }
        }


        /// <summary>
        /// 固定ハンドルネームのみ表示か判定
        /// </summary>
        public bool VisibleCustomNameOnly
        {
            get
            {
                return this.RomingProperty.VisibleCustomNameOnly;
            }

            set
            {
                this.RomingProperty.VisibleCustomNameOnly = value;
            }
        }

        /// <summary>
        /// 読み上げ有効か判定
        /// </summary>
        public bool SpeechEnable
        {
            get
            {
                return RomingProperty.SpeechEnable;
            }

            set
            {
                RomingProperty.SpeechEnable = value;
            }
        }

        /// <summary>
        /// 次枠自動取得するか判定
        /// </summary>
        public bool NextFrameAutoConnection
        {
            get
            {
                return this.RomingProperty.NextFrameAutoConnection;
            }

            set
            {
                this.RomingProperty.NextFrameAutoConnection = value;
            }
        }

        /// <summary>
        /// 次枠接続時にブラウザでも開くか判定
        /// </summary>
        public bool NextFrameAutoConnectionWithBrowser
        {
            get
            {
                return this.RomingProperty.NextFrameAutoConnectionWithBrowser;
            }

            set
            {
                this.RomingProperty.NextFrameAutoConnectionWithBrowser = value;
            }
        }

        /// <summary>
        /// コメント送信変換プロパティ
        /// </summary>
        public SubmitCommentConversions SubmitCommentConversions
        {
            get
            {
                if (saveAbleSubmitCommentConversions == null) return null;
                return saveAbleSubmitCommentConversions.Propety;
            }

            internal set
            {
                saveAbleSubmitCommentConversions.Propety = value;
            }
        }


        /// <summary>
        /// にこびゅローカル設定
        /// </summary>
        private NicoViewLocalProperty LocalProperty
        {
            get
            {
                return this.saveAblelocalProperty.Propety;
            }
        }


        public string BouyomichanPath
        {
            get
            {
                return this.LocalProperty.BouyomichanPath;
            }
            set
            {
                this.LocalProperty.BouyomichanPath = value;
            }
        }

        /// <summary>
        /// コメントNGシェアレベル
        /// </summary>
        public CommentNgShareLevel CommentNgShareLevel
        {
            get
            {
                return this.RomingProperty.CommentNgShareLevel;
            }

            set
            {
                this.RomingProperty.CommentNgShareLevel = value;
            }
        }

        /// <summary>
        /// URL解決用ブラウザ
        /// </summary>
        public UrlExecuteBrowser UrlExecuteBrowser
        {
            get
            {
                return this.LocalProperty.UrlExecuteBrowser;
            }

            set
            {
                this.LocalProperty.UrlExecuteBrowser = value;
            }
        }

        internal NicoSafeUserAuth Auth
        {
            get
            {
                return this.RomingProperty.Auth;
            }

            set
            {
                this.RomingProperty.Auth = value;
            }
        }



        /// <summary>
        /// 視聴ユーザ情報
        /// </summary>
        public NicoUserDictionary NicoUsers
        {
            get
            {
                return saveAbleNicoEncounterUsers.Propety;
            }

            internal set
            {
                saveAbleNicoEncounterUsers.Propety = value;
                this.RaisePropertyChangedAll();
            }
        }


        private SaveAbleProperty<NicoViewRomingProperty> saveAbleProperty_;

        private SaveAbleProperty<NicoViewRomingProperty> saveAbleProperty
        {
            get
            {
                return saveAbleProperty_;
            }

            set
            {
                if (saveAbleProperty_ != value)
                {
                    saveAbleProperty_ = value;
                    RaisePropertyChangedAll();
                }


            }
        }


        private SaveAbleProperty<NicoViewBroadcastProperty> saveAbleBroadcastProperty;


        private SaveAbleProperty<NicoUserDictionary> saveAbleNicoEncounterUsers;

        private SaveAbleProperty<SerializableSpeechInfo> speechInfo;

        private SaveAbleProperty<NicoViewLocalProperty> saveAblelocalProperty_;

        private SaveAbleProperty<NicoViewLocalProperty> saveAblelocalProperty
        {

            get
            {
                return saveAblelocalProperty_;
            }

            set
            {
                if (saveAblelocalProperty_ != value)
                {
                    saveAblelocalProperty_ = value;
                    RaisePropertyChangedAll();

                }
            }
        }

        private SaveAbleProperty<SubmitCommentConversions> saveAbleSubmitCommentConversions;

        #endregion


        #region メソッド

        /// <summary>
        /// 配信情報読込
        /// </summary>
        /// <returns></returns>
        public async Task LoadBroadcastPropertyAsync()
        {
            this.saveAbleBroadcastProperty =  await InternalLoadAsync<NicoViewBroadcastProperty>(FileManager.RomingLoadAsync<NicoViewBroadcastProperty>, BROADCASTPROPERTY_FOLDERNAME, BROADCASTPROPERTY_FILENAME).ConfigureAwait(false);
            
        }

        /// <summary>
        /// 配信情報の保存
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        internal async Task SaveBroadcastPropertyAsync()
        {

            await InternalSaveAsync<NicoViewBroadcastProperty>(FileManager.RomingSaveAsync, BROADCASTPROPERTY_FOLDERNAME, BROADCASTPROPERTY_FILENAME, this.saveAbleBroadcastProperty).ConfigureAwait(false);

            
        }


        /// <summary>
        /// プロパティ情報保存
        /// </summary>
        /// <param name="currentServiceProvider">保存に使用するサービスプロバイダ</param>
        internal async Task SaveAsync(NicoSafeUserAuth selfUser)
        {
            try
            {
                this.LocalProperty.UrlExecuteBrowser = Browsers.Instance.CurrentUrlExecuteBrowser.UrlExecuteBrowser;
                var userLists = new SaveAbleProperty<SerializableNicoEncounterUserList> { Propety = new SerializableNicoEncounterUserList() { UserList =  NicoUsers.AsSerialize() }, SaveFlag = saveAbleNicoEncounterUsers.SaveFlag };


                this.RomingProperty.Auth = selfUser;


                await Task.WhenAll(
                     Task.Run(async ()=>{ await InternalSaveAsync(FileManager.RomingSaveAsync,PROFILEFOLDERNAME,PROFILEFILENAME, saveAbleProperty).ConfigureAwait(false);}),
                     Task.Run(async ()=>
                     {
                         userLists.Propety.SelectionSaveUsers();
                         await InternalSaveAsync(FileManager.RomingSaveAsync,ENCOUNTERUSER_FOLDERNAME,ENCOUNTERUSER_FILENAME,userLists).ConfigureAwait(false);
                     }),
                     Task.Run(async ()=>{await InternalSaveAsync(FileManager.LocalSaveAsync,LOCALDATAFOLDERNAME,LOCALDATAFILENAME,saveAblelocalProperty).ConfigureAwait(false);}),
                     Task.Run(async()=>
                     {
                         speechInfo.Propety = NicoViewSpeacherApplication.Current.AsSave();
                         await InternalSaveAsync(FileManager.RomingSaveAsync,SPEECHINFO_FOLDERNAME,SPEECHINFO_FILENAME,speechInfo).ConfigureAwait(false);
                     }),
                     Task.Run(()=>
                     {
                         //NG回避機能はまだファイル保存されないためコメントアウト
                         //InternalSaveAsync(FileManager.RomingSaveAsync, SUBMITCONVDATAFOLDERNAME, SUBMITCONVDATAFILENAME, _saveAbleSubmitCommentConversions).Wait();
                     }),
                     SaveBroadcastPropertyAsync()
                ).ConfigureAwait(false);
            }
            catch (NicoException e)
            {
                throw e;
            }
            catch (Exception e)
            {
                new NicoException(e);
            }
        }

        

        /// <summary>
        /// プロパティ情報読込
        /// </summary>
        internal async Task PropertysLoadAsync()
        {
            await s_commonLoadAction(async () =>
            {

                await Task.WhenAll(

                        Task.Run(async () =>
                        {
                            saveAblelocalProperty = await InternalLoadAsync<NicoViewLocalProperty>(FileManager.LocalLoadAsync<NicoViewLocalProperty>, LOCALDATAFOLDERNAME, LOCALDATAFILENAME).ConfigureAwait(false);
                        }),
                        Task.Run(async () =>
                        {
                            saveAbleProperty = await InternalLoadAsync<NicoViewRomingProperty>(FileManager.RomingLoadAsync<NicoViewRomingProperty>, PROFILEFOLDERNAME, PROFILEFILENAME).ConfigureAwait(false);
                        })
                ).ConfigureAwait(false);

                
            }).ConfigureAwait(false);
            
        }

        /// <summary>
        /// 遅延可能なユーザ設定読込
        /// </summary>
        internal async Task UserDataLoadAsync()
        {

            await s_commonLoadAction(async () =>
            {
                await Task.WhenAll(

                     Task.Run(async () =>
                     {
                         var userList = await InternalLoadAsync<SerializableNicoEncounterUserList>(FileManager.RomingLoadAsync<SerializableNicoEncounterUserList>, ENCOUNTERUSER_FOLDERNAME, ENCOUNTERUSER_FILENAME);
                         this.saveAbleNicoEncounterUsers = new SaveAbleProperty<NicoUserDictionary>() { Propety = new NicoUserDictionary(), SaveFlag = userList.SaveFlag };

                         NicoUsers.Set(userList.Propety.UserList);
                     }),
                     Task.Run(async () =>
                     {
                         speechInfo = await InternalLoadAsync<SerializableSpeechInfo>(FileManager.RomingLoadAsync<SerializableSpeechInfo>, SPEECHINFO_FOLDERNAME, SPEECHINFO_FILENAME).ConfigureAwait(false);

                         NicoViewSpeacherApplication.Current.Set(speechInfo.Propety);

                     }),
                     Task.Run(() =>
                     {

                         //NGコメント回避機能はまだファイル保存機能ではないため、生成する
                         saveAbleSubmitCommentConversions = new SaveAbleProperty<SubmitCommentConversions>();
                         saveAbleSubmitCommentConversions.Propety = new Comments.SubmitCommentConversions();
                         saveAbleSubmitCommentConversions.Propety.AddDefaultCase();
                         // _saveAbleSubmitCommentConversions = InternalLoadAsync<SubmitCommentConversions>(FileManager.RomingLoadAsync<SubmitCommentConversions>, SUBMITCONVDATAFOLDERNAME, SUBMITCONVDATAFILENAME).GetAwaiter().GetResult();
                     })

                ).ConfigureAwait(false);
            });

                

                

        }


        private static async Task s_commonLoadAction(Func<Task> func)
        {
            try
            {
                await func().ConfigureAwait(false);
            }
            catch (NicoException e)
            {
                throw e;
            }
            catch (Exception e)
            {
                new NicoException(e);
            }
        }


        /// <summary>
        /// 内部で使用される共通保存操作
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="saveFunc"></param>
        /// <param name="folderName"></param>
        /// <param name="fileName"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        internal static async Task InternalSaveAsync<T>(Func<string, string, T, Action<IOException>, Action<Exception>, Task> saveFunc, string folderName, string fileName, SaveAbleProperty<T> target)
        {
            if (target == null ||  !target.SaveFlag || target.Propety == null) return;
            await saveFunc(folderName, fileName, target.Propety, async (e) =>
            {
                await ErrorManager.SaveExceptionAsync(e);
            }, async (e) =>
            {
                await ErrorManager.SaveExceptionAsync(e);
            }).ConfigureAwait(false);
        }



        /// <summary>
        /// プロパティ情報をまとめて更新
        /// </summary>
        internal void UpdatePropertys()
        {
            Browsers.Instance.RaisePropertyChangedAll();
            RomingProperty.RaisePropertyChangedAll();
            LocalProperty.RaisePropertyChangedAll();
        }


        /// <summary>
        /// 内部で使用される共通読込操作
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="loadFunc"></param>
        /// <param name="folderName"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        internal static async Task<SaveAbleProperty<T>> InternalLoadAsync<T>(Func<string, string, Action<IOException>, Action<Exception>, Task<T>> loadFunc, string folderName, string fileName)
            where T : new()
        {
            SaveAbleProperty<T> property = new SaveAbleProperty<T>();
            property.Propety = await loadFunc(folderName, fileName, async (e) =>
            {
                await ErrorManager.SaveExceptionAsync(e);
                property.SaveFlag = false;
            },
                async (e) =>
                {
                    await ErrorManager.SaveExceptionAsync(e);
                });

            return property;
        }

        #endregion

        #region 定数

        internal const string USERAUTHFOLDERNAME = "UserAuthData";

        internal const string USERAUTHFILENAME = "UserAuthData_v1.bs";

        internal const string PROFILEFOLDERNAME = "Profile";

        internal const string PROFILEFILENAME = "ProfileData_v1.bs";

        internal const string ENCOUNTERUSER_FOLDERNAME = "EncounterUser";

        internal const string ENCOUNTERUSER_FILENAME = "EncounterUser_v1.bs";

        internal const string SPEECHINFO_FOLDERNAME = "SpeechData";

        internal const string SPEECHINFO_FILENAME = "SpeechData_v1.bs";

        internal const string LOCALDATAFOLDERNAME = "LocalData";

        internal const string LOCALDATAFILENAME = "LocalData_v1.bs";

        internal const string SUBMITCONVDATAFOLDERNAME = "SubmitConvData";

        internal const string SUBMITCONVDATAFILENAME = "SubmitConvData.bs";

        internal const string BROADCASTPROPERTY_FOLDERNAME = "Broacast";

        internal const string BROADCASTPROPERTY_FILENAME = "BroadcastData_v1.bs";

        #endregion
    }
}
